Passed
Pull Request — master (#57)
by
unknown
11:34 queued 08:49
created

scrollspy.js ➔ refresh   C

Complexity

Conditions 11

Size

Total Lines 38
Code Lines 26

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
cc 11
eloc 26
c 0
b 0
f 0
dl 0
loc 38
rs 5.4

How to fix   Complexity   

Complexity

Complex classes like scrollspy.js ➔ refresh often do a lot of different things. To break such a class down, we need to identify a cohesive component within that class. A common approach to find such a component is to look for fields/methods that share the same prefixes, or suffixes.

Once you have determined the fields that belong together, you can apply the Extract Class refactoring. If the component makes sense as a sub-class, Extract Subclass is also a candidate, and is often faster.

1
/*!
2
  * Bootstrap scrollspy.js v4.6.1 (https://getbootstrap.com/)
3
  * Copyright 2011-2021 The Bootstrap Authors (https://github.com/twbs/bootstrap/graphs/contributors)
4
  * Licensed under MIT (https://github.com/twbs/bootstrap/blob/main/LICENSE)
5
  */
6
(function (global, factory) {
7
  typeof exports === 'object' && typeof module !== 'undefined' ? module.exports = factory(require('jquery'), require('./util.js')) :
8
  typeof define === 'function' && define.amd ? define(['jquery', './util'], factory) :
9
  (global = typeof globalThis !== 'undefined' ? globalThis : global || self, global.ScrollSpy = factory(global.jQuery, global.Util));
10
})(this, (function ($, Util) { 'use strict';
11
12
  function _interopDefaultLegacy (e) { return e && typeof e === 'object' && 'default' in e ? e : { 'default': e }; }
13
14
  var $__default = /*#__PURE__*/_interopDefaultLegacy($);
15
  var Util__default = /*#__PURE__*/_interopDefaultLegacy(Util);
16
17
  function _defineProperties(target, props) {
18
    for (var i = 0; i < props.length; i++) {
19
      var descriptor = props[i];
20
      descriptor.enumerable = descriptor.enumerable || false;
21
      descriptor.configurable = true;
22
      if ("value" in descriptor) descriptor.writable = true;
23
      Object.defineProperty(target, descriptor.key, descriptor);
24
    }
25
  }
26
27
  function _createClass(Constructor, protoProps, staticProps) {
28
    if (protoProps) _defineProperties(Constructor.prototype, protoProps);
29
    if (staticProps) _defineProperties(Constructor, staticProps);
30
    return Constructor;
31
  }
32
33
  function _extends() {
34
    _extends = Object.assign || function (target) {
35
      for (var i = 1; i < arguments.length; i++) {
36
        var source = arguments[i];
37
38
        for (var key in source) {
39
          if (Object.prototype.hasOwnProperty.call(source, key)) {
40
            target[key] = source[key];
41
          }
42
        }
43
      }
44
45
      return target;
46
    };
47
48
    return _extends.apply(this, arguments);
49
  }
50
51
  /**
52
   * Constants
53
   */
54
55
  var NAME = 'scrollspy';
56
  var VERSION = '4.6.1';
57
  var DATA_KEY = 'bs.scrollspy';
58
  var EVENT_KEY = "." + DATA_KEY;
59
  var DATA_API_KEY = '.data-api';
60
  var JQUERY_NO_CONFLICT = $__default["default"].fn[NAME];
61
  var CLASS_NAME_DROPDOWN_ITEM = 'dropdown-item';
62
  var CLASS_NAME_ACTIVE = 'active';
63
  var EVENT_ACTIVATE = "activate" + EVENT_KEY;
64
  var EVENT_SCROLL = "scroll" + EVENT_KEY;
65
  var EVENT_LOAD_DATA_API = "load" + EVENT_KEY + DATA_API_KEY;
66
  var METHOD_OFFSET = 'offset';
67
  var METHOD_POSITION = 'position';
68
  var SELECTOR_DATA_SPY = '[data-spy="scroll"]';
69
  var SELECTOR_NAV_LIST_GROUP = '.nav, .list-group';
70
  var SELECTOR_NAV_LINKS = '.nav-link';
71
  var SELECTOR_NAV_ITEMS = '.nav-item';
72
  var SELECTOR_LIST_ITEMS = '.list-group-item';
73
  var SELECTOR_DROPDOWN = '.dropdown';
74
  var SELECTOR_DROPDOWN_ITEMS = '.dropdown-item';
75
  var SELECTOR_DROPDOWN_TOGGLE = '.dropdown-toggle';
76
  var Default = {
77
    offset: 10,
78
    method: 'auto',
79
    target: ''
80
  };
81
  var DefaultType = {
82
    offset: 'number',
83
    method: 'string',
84
    target: '(string|element)'
85
  };
86
  /**
87
   * Class definition
88
   */
89
90
  var ScrollSpy = /*#__PURE__*/function () {
91
    function ScrollSpy(element, config) {
92
      var _this = this;
93
94
      this._element = element;
95
      this._scrollElement = element.tagName === 'BODY' ? window : element;
96
      this._config = this._getConfig(config);
97
      this._selector = this._config.target + " " + SELECTOR_NAV_LINKS + "," + (this._config.target + " " + SELECTOR_LIST_ITEMS + ",") + (this._config.target + " " + SELECTOR_DROPDOWN_ITEMS);
98
      this._offsets = [];
99
      this._targets = [];
100
      this._activeTarget = null;
101
      this._scrollHeight = 0;
102
      $__default["default"](this._scrollElement).on(EVENT_SCROLL, function (event) {
103
        return _this._process(event);
104
      });
105
      this.refresh();
106
107
      this._process();
108
    } // Getters
109
110
111
    var _proto = ScrollSpy.prototype;
112
113
    // Public
114
    _proto.refresh = function refresh() {
115
      var _this2 = this;
116
117
      var autoMethod = this._scrollElement === this._scrollElement.window ? METHOD_OFFSET : METHOD_POSITION;
118
      var offsetMethod = this._config.method === 'auto' ? autoMethod : this._config.method;
119
      var offsetBase = offsetMethod === METHOD_POSITION ? this._getScrollTop() : 0;
120
      this._offsets = [];
121
      this._targets = [];
122
      this._scrollHeight = this._getScrollHeight();
123
      var targets = [].slice.call(document.querySelectorAll(this._selector));
124
      targets.map(function (element) {
125
        var target;
126
        var targetSelector = Util__default["default"].getSelectorFromElement(element);
127
128
        if (targetSelector) {
129
          target = document.querySelector(targetSelector);
130
        }
131
132
        if (target) {
133
          var targetBCR = target.getBoundingClientRect();
134
135
          if (targetBCR.width || targetBCR.height) {
136
            // TODO (fat): remove sketch reliance on jQuery position/offset
137
            return [$__default["default"](target)[offsetMethod]().top + offsetBase, targetSelector];
138
          }
139
        }
140
141
        return null;
142
      }).filter(function (item) {
143
        return item;
144
      }).sort(function (a, b) {
145
        return a[0] - b[0];
146
      }).forEach(function (item) {
147
        _this2._offsets.push(item[0]);
148
149
        _this2._targets.push(item[1]);
150
      });
151
    };
152
153
    _proto.dispose = function dispose() {
154
      $__default["default"].removeData(this._element, DATA_KEY);
155
      $__default["default"](this._scrollElement).off(EVENT_KEY);
156
      this._element = null;
157
      this._scrollElement = null;
158
      this._config = null;
159
      this._selector = null;
160
      this._offsets = null;
161
      this._targets = null;
162
      this._activeTarget = null;
163
      this._scrollHeight = null;
164
    } // Private
165
    ;
166
167
    _proto._getConfig = function _getConfig(config) {
168
      config = _extends({}, Default, typeof config === 'object' && config ? config : {});
169
170
      if (typeof config.target !== 'string' && Util__default["default"].isElement(config.target)) {
171
        var id = $__default["default"](config.target).attr('id');
172
173
        if (!id) {
174
          id = Util__default["default"].getUID(NAME);
175
          $__default["default"](config.target).attr('id', id);
176
        }
177
178
        config.target = "#" + id;
179
      }
180
181
      Util__default["default"].typeCheckConfig(NAME, config, DefaultType);
182
      return config;
183
    };
184
185
    _proto._getScrollTop = function _getScrollTop() {
186
      return this._scrollElement === window ? this._scrollElement.pageYOffset : this._scrollElement.scrollTop;
187
    };
188
189
    _proto._getScrollHeight = function _getScrollHeight() {
190
      return this._scrollElement.scrollHeight || Math.max(document.body.scrollHeight, document.documentElement.scrollHeight);
191
    };
192
193
    _proto._getOffsetHeight = function _getOffsetHeight() {
194
      return this._scrollElement === window ? window.innerHeight : this._scrollElement.getBoundingClientRect().height;
195
    };
196
197
    _proto._process = function _process() {
198
      var scrollTop = this._getScrollTop() + this._config.offset;
199
200
      var scrollHeight = this._getScrollHeight();
201
202
      var maxScroll = this._config.offset + scrollHeight - this._getOffsetHeight();
203
204
      if (this._scrollHeight !== scrollHeight) {
205
        this.refresh();
206
      }
207
208
      if (scrollTop >= maxScroll) {
209
        var target = this._targets[this._targets.length - 1];
210
211
        if (this._activeTarget !== target) {
212
          this._activate(target);
213
        }
214
215
        return;
216
      }
217
218
      if (this._activeTarget && scrollTop < this._offsets[0] && this._offsets[0] > 0) {
219
        this._activeTarget = null;
220
221
        this._clear();
222
223
        return;
224
      }
225
226
      for (var i = this._offsets.length; i--;) {
227
        var isActiveTarget = this._activeTarget !== this._targets[i] && scrollTop >= this._offsets[i] && (typeof this._offsets[i + 1] === 'undefined' || scrollTop < this._offsets[i + 1]);
228
229
        if (isActiveTarget) {
230
          this._activate(this._targets[i]);
231
        }
232
      }
233
    };
234
235
    _proto._activate = function _activate(target) {
236
      this._activeTarget = target;
237
238
      this._clear();
239
240
      var queries = this._selector.split(',').map(function (selector) {
241
        return selector + "[data-target=\"" + target + "\"]," + selector + "[href=\"" + target + "\"]";
242
      });
243
244
      var $link = $__default["default"]([].slice.call(document.querySelectorAll(queries.join(','))));
245
246
      if ($link.hasClass(CLASS_NAME_DROPDOWN_ITEM)) {
247
        $link.closest(SELECTOR_DROPDOWN).find(SELECTOR_DROPDOWN_TOGGLE).addClass(CLASS_NAME_ACTIVE);
248
        $link.addClass(CLASS_NAME_ACTIVE);
249
      } else {
250
        // Set triggered link as active
251
        $link.addClass(CLASS_NAME_ACTIVE); // Set triggered links parents as active
252
        // With both <ul> and <nav> markup a parent is the previous sibling of any nav ancestor
253
254
        $link.parents(SELECTOR_NAV_LIST_GROUP).prev(SELECTOR_NAV_LINKS + ", " + SELECTOR_LIST_ITEMS).addClass(CLASS_NAME_ACTIVE); // Handle special case when .nav-link is inside .nav-item
255
256
        $link.parents(SELECTOR_NAV_LIST_GROUP).prev(SELECTOR_NAV_ITEMS).children(SELECTOR_NAV_LINKS).addClass(CLASS_NAME_ACTIVE);
257
      }
258
259
      $__default["default"](this._scrollElement).trigger(EVENT_ACTIVATE, {
260
        relatedTarget: target
261
      });
262
    };
263
264
    _proto._clear = function _clear() {
265
      [].slice.call(document.querySelectorAll(this._selector)).filter(function (node) {
266
        return node.classList.contains(CLASS_NAME_ACTIVE);
267
      }).forEach(function (node) {
268
        return node.classList.remove(CLASS_NAME_ACTIVE);
269
      });
270
    } // Static
271
    ;
272
273
    ScrollSpy._jQueryInterface = function _jQueryInterface(config) {
274
      return this.each(function () {
275
        var data = $__default["default"](this).data(DATA_KEY);
276
277
        var _config = typeof config === 'object' && config;
278
279
        if (!data) {
280
          data = new ScrollSpy(this, _config);
281
          $__default["default"](this).data(DATA_KEY, data);
282
        }
283
284
        if (typeof config === 'string') {
285
          if (typeof data[config] === 'undefined') {
286
            throw new TypeError("No method named \"" + config + "\"");
287
          }
288
289
          data[config]();
290
        }
291
      });
292
    };
293
294
    _createClass(ScrollSpy, null, [{
295
      key: "VERSION",
296
      get: function get() {
297
        return VERSION;
298
      }
299
    }, {
300
      key: "Default",
301
      get: function get() {
302
        return Default;
303
      }
304
    }]);
305
306
    return ScrollSpy;
307
  }();
308
  /**
309
   * Data API implementation
310
   */
311
312
313
  $__default["default"](window).on(EVENT_LOAD_DATA_API, function () {
314
    var scrollSpys = [].slice.call(document.querySelectorAll(SELECTOR_DATA_SPY));
315
    var scrollSpysLength = scrollSpys.length;
316
317
    for (var i = scrollSpysLength; i--;) {
318
      var $spy = $__default["default"](scrollSpys[i]);
319
320
      ScrollSpy._jQueryInterface.call($spy, $spy.data());
321
    }
322
  });
323
  /**
324
   * jQuery
325
   */
326
327
  $__default["default"].fn[NAME] = ScrollSpy._jQueryInterface;
328
  $__default["default"].fn[NAME].Constructor = ScrollSpy;
329
330
  $__default["default"].fn[NAME].noConflict = function () {
331
    $__default["default"].fn[NAME] = JQUERY_NO_CONFLICT;
332
    return ScrollSpy._jQueryInterface;
333
  };
334
335
  return ScrollSpy;
336
337
}));
338
//# sourceMappingURL=scrollspy.js.map
339